package cacheClient

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"github.com/go-redis/redis"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strconv"
	"strings"
)

type Cmd struct {
	Name string	// 操作的类型
	Key string	// 键
	Value string	// 值
	Error error
}

type Client interface {
	Run(*Cmd)
	PipelinedRun([]*Cmd)
}

func New(typ,server string) Client  {
	if typ == "redis" {
		return newRedisClient(server)
	}
	if typ == "http" {
		return newHTTPClient(server)
	}
	if typ == "tcp" {
		return newTCPClient(server)
	}
	panic("unknown client type" + typ)
}

type httpClient struct {
	*http.Client
	server string
}

func (c *httpClient) PipelineRun(cmds []*Cmd) {
	panic("implement me")
}

// 缓存get操作
func (c *httpClient) get(key string) string  {
	// 根据缓存服务的地址和key拼成URL并发送HTTP GET请求
	resp,e := c.Get(c.server + key)
	if e != nil {
		log.Println(key)
		panic(e)
	}
	if resp.StatusCode == http.StatusNotFound {
		return ""
	}
	if resp.StatusCode != http.StatusOK {
		panic(resp.Status)
	}
	b,e := ioutil.ReadAll(resp.Body)
	if e != nil {
		panic(e)
	}
	return string(b)
}

// 发送HTTP PUT请求实现缓存Set操作
func (c *httpClient) set(key,value string)  {
	req,e := http.NewRequest(http.MethodPut,
		c.server+key,strings.NewReader(value))
	if e != nil {
		log.Println(key)
		panic(e)
	}
	resp,e := c.Do(req)
	if e != nil {
		log.Println(key)
		panic(e)
	}
	if resp.StatusCode != http.StatusOK {
		panic(resp.Status)
	}
	
}

// 但一个运行Cmd
func (c *httpClient) Run(cmd *Cmd)  {
	if cmd.Name == "get" {
		cmd.Value = c.get(cmd.Key)
		return
	}
	if cmd.Name == "set" {
		c.set(cmd.Key,cmd.Value)
		return
	}
	panic("unknown cmd name" + cmd.Name)
}

func newHTTPClient(server string) *httpClient  {
	client := &http.Client{Transport: &http.Transport{MaxIdleConnsPerHost: 1}}
	return &httpClient{client,"http://"+server+":12345/cache/"}

}

func (c *httpClient) PipelinedRun([]*Cmd)  {
	panic("httpClient pipelined run not implement")
}

type tcpClient struct {
	net.Conn
	r *bufio.Reader
}

func (c *tcpClient) PipelineRun(cmds []*Cmd) {
	panic("implement me")
}

// 发送G command到服务端
func (c *tcpClient) sendGet(key string)  {
	klen := len(key)
	c.Write([]byte(fmt.Sprintf("G%d %s",klen,key)))
	bbb := fmt.Sprintf("%d %s",klen,key)
	c.r = bufio.NewReader(bytes.NewReader([]byte(bbb)))
}

func (c *tcpClient)sendSet (key,value string) {
	klen := len(key)
	vlen := len(value)
	c.Write([]byte(fmt.Sprintf("S%d %d %s%s",klen,vlen,key,value)))
	bbb := fmt.Sprintf("%d %d %s%s",klen,vlen,key,value)
	c.r = bufio.NewReader(bytes.NewReader([]byte(bbb)))
}

func (c *tcpClient) sendDel(key string)  {
	klen := len(key)
	c.Write([]byte(fmt.Sprintf("D%d %s",klen,key)))
	bbb := fmt.Sprintf("%d %s",klen,key)
	c.r = bufio.NewReader(bytes.NewReader([]byte(bbb)))

}

func readLen(r *bufio.Reader) int  {
	tmp,e := r.ReadString(' ')
	if e != nil {
		log.Println(e)
		return 0
	}
	l,e := strconv.Atoi(strings.TrimSpace(tmp))
	if e != nil {
		log.Println(tmp,e)
		return 0
	}
	return l
}

// 接收和解析来自服务端的响应
func (c *tcpClient) recvResponse() (string,error)  {
	vlen := readLen(c.r)
	if vlen == 0 {
		return "",nil
	}
	if vlen < 0 {
		err := make([]byte,-vlen)
		_,e := io.ReadFull(c.r,err)
		if e != nil {
			return "",e
		}
		return "",errors.New(string(err))
	}
	value := make([]byte,vlen)
	_,e := io.ReadFull(c.r,value)
	if e != nil {
		return "",e
	}
	return string(value),nil
}

func (c *tcpClient)Run(cmd *Cmd)  {
	if cmd.Name == "get" {
		c.sendGet(cmd.Key)
		cmd.Value,cmd.Error = c.recvResponse()
		return
	}
	if cmd.Name == "set" {
		c.sendSet(cmd.Key,cmd.Value)
		_,cmd.Error = c.recvResponse()
		return
	}
	if cmd.Name == "del" {
		c.sendDel(cmd.Key)
		_,cmd.Error = c.recvResponse()
		return
	}
	panic("unknown cmd name "+cmd.Name)
}

// 批量运行Cmd
func (c *tcpClient) PipelinedRun(cmds []*Cmd)  {
	if len(cmds) == 0 {
		return
	}
	// 根据Cmd的Name成员变量调用相应的send方法
	for _,cmd := range cmds {
		if cmd.Name == "get" {
			c.sendGet(cmd.Key)
		}
		if cmd.Name == "set" {
			c.sendSet(cmd.Key,cmd.Value)
		}
		if cmd.Name == "del" {
			c.sendDel(cmd.Key)
		}
	}
	// 接收响应
	for _,cmd := range cmds {
		cmd.Value,cmd.Error = c.recvResponse()
	}
}

func newTCPClient(server string) *tcpClient  {
	// 连接服务器的指定TCP端口
	c,e := net.Dial("tcp",server+":12346")
	if e != nil {
		panic(e)
	}
	r := bufio.NewReader(c)
	return &tcpClient{c,r}
}

type redisClient struct {
	*redis.Client
}

func (r *redisClient) PipelineRun(cmds []*Cmd) {
	panic("implement me")
}

func (r *redisClient) get(key string) (string,error)  {
	res,e := r.Get(key).Result()
	if e == redis.Nil {
		return "",nil
	}
	return res,e
}

func (r *redisClient) set(key,value string) error  {
	return r.Set(key,value,0).Err()
}

func (r *redisClient) del(key string) error  {
	return r.Del(key).Err()
}

func (r *redisClient) Run(c *Cmd)  {
	if c.Name == "get" {
		c.Value,c.Error = r.get(c.Key)
		return
	}
	if c.Name == "set" {
		c.Error = r.set(c.Key,c.Value)
		return
	}
	if c.Name == "del" {
		c.Error = r.del(c.Key)
		return
	}
	panic("unknown cmd name "+c.Name)
}

func (r *redisClient) PipelinedRun(cmds []*Cmd)  {
	if len(cmds) == 0 {
		return
	}
	pipe := r.Pipeline()
	cmders := make([]redis.Cmder,len(cmds))
	for i,c := range cmds {
		if c.Name == "get" {
			cmders[i] = pipe.Get(c.Key)
		} else if c.Name == "set" {
			cmders[i] = pipe.Set(c.Key,c.Value,0)
		} else if c.Name == "del" {
			cmders[i] = pipe.Del(c.Key)
		} else {
			panic("unknown cmd name "+c.Name)
		}
	}
	_,e := pipe.Exec()
	if e != nil && e != redis.Nil {
		panic(e)
	}
	for i,c := range cmds {
		if c.Name == "get" {
			value,e := cmders[i].(*redis.StringCmd).Result()
			if e == redis.Nil {
				value,e = "",nil
			}
			c.Value,c.Error = value,e
		} else {
			c.Error = cmders[i].Err()
		}
	}
}

func newRedisClient(server string) *redisClient  {
	return &redisClient{redis.NewClient(&redis.Options{Addr:server+ ":6379",ReadTimeout:-1})}
}


